#! /usr/bin/perl

use FindBin qw($Bin);
use lib "$Bin/../lib";

###################################################################
# CONFIGURATION 
# the path to your maude 2 binary
#$maudepath = "maude.darwin";
##$maudepath = "maude";
$maudepath = "/home/software/maude22/maude.linux";
##################################################################

$tmpfile="/tmp/foo1";
$pgmfile = "$Bin/JavaCFG.maude";


sub strip($){
	my $string = shift;
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
	return $string;
}

my $cfg;
my $res = "";

my $filename;
my $dot_input;
my $maude_input;
my $clone;

@terminals = ();
@nonTerminals = ();
@terminals2Internal = ();
@event2Action = ();
@nonTerminals2Internal = ();
$moreStateDecls = "";

$startSymbol = "";

$formulaFlag = 0;

sub handleFormulaLine($){
  my @syms =  split(" ", shift);
  foreach(@syms){
	if($terminals2Internal{$_}){
	  $cfg .= " $terminals2Internal{$_} ";
	}
	elsif($_ eq "->"){
	   $cfg .= " -> ";
	}
	elsif($_ eq ";"){
	   $formulaFlag = 0;
	}
	elsif($_ eq "|"){
      $cfg .= " | ";
	}
	elsif($_ eq "epsilon"){
      $cfg .= " . ";
	}
	else{ #must be a nonTerminal
	  if($nonTerminals2Internal{$_}){
		$cfg .= " $nonTerminals2Internal{$_} ";
	  }
	  else{
        $nonTerminals[$#nonTerminals+1] = $_;
		$nonTerminals2Internal{$_} = "nt($#nonTerminals)";
		$cfg .= " nt($#nonTerminals) ";
	  }
	}
  }
  if($formulaFlag){
    $cfg .= " ;\n ";
  }
}


while (<>){
	#remove // style comments
	s/\/\/.*//;
	#put spaces in front and behind -> or | or ;
	s/([-][>]|[|]|[;])/ $1 /g;
	if ($formulaFlag == 0){
	  if(/[cC]lone\s*\;/){
        $clone = 1;
	  }
	  elsif(/\[([^\]]*)\]/){
        $moreStateDecls = "  ".$1;
		$moreStateDecls =~ s/;/;\n/g;
	  }
	  elsif (/[eE]vent\s+(.+)\;/){
		my $event = strip($1);
		my $action = "";
        #$event =~ s/([^<>]*)<([^>]*)>(.*)/$1>$2<$3/;
		if($event =~ /{(.*)}/){
		  $action = $1;
	      $event =~ s/{.*}//;
	    }
	    $event =~ s/:/[/;
		$event =~ /([^<]+)/;
		$1 =~ /([^[]+)/;
		my $term = strip($1);
		
		$terminals[$#terminals+1] = $event;
        $terminals2Internal{$term} = "t($#terminals)";
		$event2Action{$event} = "\n  ".$action;
        #print "!!!!\n".$event."!!!!\n";
	  }
	  elsif (/[sS]tart\s+[sS]ymbol\s*:(.*)\;/){
		$startSymbol = strip($1);
		$nonTerminals[$#nonTerminals+1] = $startSymbol;
		$nonTerminals2Internal{$startSymbol} = "nt($#nonTerminals)";
	  }
	  elsif (/[fF]ormula\s*\:(.*)$/){
        $formulaFlag = 1;
	    handleFormulaLine($1);
	  }
    }
	else{ #this is another formula line
	  handleFormulaLine($_);
	}
}

sub exec_maude();
sub create_output();

exec_maude();
create_output();

sub exec_maude(){
  open MAUDEIN, "|$maudepath > $tmpfile.mout" || die "Cannot execute $maudepath\n";
  print MAUDEIN "in $pgmfile\n red genJava($nonTerminals2Internal{$startSymbol}, $cfg) .";
  close MAUDEIN;

  open MAUDEOUT, "<$tmpfile.mout" || die "Cannot open /tmp/cfg.out\n";
  my $flag = 0;
  while(<MAUDEOUT>){
	if(/^result Code:(.*)$/){
      $res = $1;
	  $flag = 1;
	}
	else {
	  if(/Bye/){
		$flag = 0;
	  }
	  if($flag){
        $res = $res.$_;   
	  }
    } 
  }
  
  close MAUDEOUT;
  unlink "$tmpfile.mout";
}

sub create_output(){
  $res =~ s/int/\nint/g;

  my $monitoredEvents;
  
  $monitoredEvents 
    = "! ".$terminals[0]."]: {\n  input = 1;".$event2Action{$terminals[0]}."\n}\n"; 
  for($i = 1 ; $i <= $#terminals ; $i++){
	 $monitoredEvents .= $terminals[$i]."]: {\n  input = ".($i + 1).";".
	 $event2Action{$terminals[$i]}."\n}\n";
  }
 
  my $stackName = "\$stack";
  my $endStackDcl = "";
  my $ifCloneDcl = "";
  if($clone){
	  $stackName = "\$end_stack";
	  $endStackDcl = "\$end_stack = \$stack.fclone();"; 
      $ifCloneDcl = "\@STACK<Integer> \$end_stack;"; 
  }

my $output1 = <<DONE;
//Monitored Events
$monitoredEvents

//State Declaration
 \@STACK<Integer> \$stack = new \@STACK<Integer>();
 $ifCloneDcl
 int \$done = 0;
 int input;
 $moreStateDecls
DONE

print $output1;
$res =~ s/\n/ /g;
$res =~ s/static/\nstatic/g;
print $res."\n";

my $output2 = <<DONE;
//Initialization
\$stack.push(0);

//Reset
\$stack.clear(0);
\$stack.push(0);


//Monitoring Body
int state;
int next_state;
int old_state;
int non_terminal;

END:
while(true){
  state = \$stack.peek();
  /* state can be -2 if gt[previous_state][non_terminal] == -2 
   * on previous iteration of outer loop
   */
  if(state == -2){
    \$done = -2;
    \$stack.clear();
	\$stack.push(0);
    break;
  } 
  /* shift */
  else if(at[state][input][0] == 0){
    next_state = at[state][input][1];
    /* invalid input at this state, return violation */ 
    if(next_state == -2){
      \$done = -2;
	  \$stack.clear();
	  \$stack.push(0);
      break;
    }
    /* shift to a valid state */
    else{
      \$stack.push(input);
      \$stack.push(next_state);
      /* check for an unbounded number of reductions or an accept
       * with 0 (end of input) as the lookahead
       */
      if(at[next_state][0][0] == 1){ 
	    $endStackDcl
        while(true){
          state = $stackName.peek();
          /* state can be -2 if gt[previous_state][non_terminal] == -2 on previous
           * iteration of inner loop
           */
          if(state == -2){
            break END;
          }
          /* shift(-2) for 0, this is a default, simply break */
          else if(at[state][0][0] == 0){
            break END;
          }
          /* reduction with 0 as lookahead */
          else if(at[state][0][0] == 1){
            non_terminal = at[state][0][1];
            $stackName.pop(at[state][0][2]);
            old_state = $stackName.peek();
            $stackName.push(non_terminal);
            $stackName.push(gt[old_state][non_terminal]); 
          }
          /* accept with 0 as lookahead */
          else if(at[state][0][0] == 2){	
            \$done = -1;
            break END;
          }
          /* algorithm error? */
          else{  
            \$done = -3;
            break END;
          }
        }
      }
	  else{ /*due to the nature of the table construction accept without reduction is impossible*/
        break END;
      }
    }
  }
  /* reduce
   * note that we don't check for 0 as an input (i.e. index 0 for input)
   * because any reduction chain with non-0 lookahead must end with
   * a shift or an error, i.e. we can never stop with a reduce, otherwise
   * we would lose our event.  All events must be shifted or cause a violation
   */
  else if(at[state][input][0] == 1){
    non_terminal = at[state][input][1];
    \$stack.pop(at[state][input][2]);
    old_state = \$stack.peek();
    \$stack.push(non_terminal);
    \$stack.push(gt[old_state][non_terminal]);
  }
  /* algorithm error? (because 0 cannot be an input) */
  else{
    \$done = -3;
    break;
  }
}


//Success condition
 \$done == -1;

//Failure condition
 \$done == -2;

DONE

print $output2;
 
}

## test
#test_action();
#test_cfg();
#test_term();
#test_nonTerm();
#sub test_action(){
# print $event2Action{"a"};
#}
sub test_cfg(){
  print $cfg."\n****\n";
}
sub test_term(){
  foreach(@terminals){
	print;
    print "\n";
	print $terminals2Internal{$_}."\n";
  }
  print "***\n";
}
sub test_nonTerm(){
  foreach(@nonTerminals){
	print;
    print "\n";
	print $nonTerminals2Internal{$_}."\n";
  }
  print "RESULT\n $res\n";
}
